<script lang="ts" setup>
import { ref, computed, watch, onMounted, onBeforeMount } from 'vue'

import { parseDate, formatDate } from '@/utils/util'

type Data = Record<string, { text: string; status: number }> | undefined

type DateItem = {
  value: string // 年-月-日
  date: number // 日期
  text?: string // 文本
  status?: number // 状态
  today?: boolean // 是否今天
  before?: boolean // 是否之前
  selected?: boolean // 是否选中
  disabled: boolean // 是否禁用
}

type DateList = DateItem[]

type ModeType = '10' | '06' // '10'=>周一到周天, '06'=>周天到周六

// TODO: 优化，自定义状态和关联颜色(状态默认三种)
const props = withDefaults(
  defineProps<{
    mode?: ModeType
    data: Data
    default?: string | undefined
  }>(),
  { mode: '06' }
)

const emit = defineEmits<{
  (e: 'confirm', value: string[]): void
  (
    e: 'select',
    value: { value: string; status: number; before?: boolean }
  ): void
}>()

let minDate: Date
let maxDate: Date
let today: string[]

const show = ref(false)
const current = ref<string[]>([])
const selected = ref<string[]>([])
const list = ref<DateList>([])

const seletedText = computed(
  () => `${selected.value[0]}年${selected.value[1]}月`
)
const currentText = computed(() => `${current.value[0]}年${current.value[1]}月`)

watch(
  () => props.data,
  (value) => value && init()
)

onBeforeMount(() => {
  // TODO: 优化，将最大最小日期和当前日期开放出来
  today = formatDate(new Date(), 'yyyy-MM-dd').split('-')
  minDate = new Date(+today[0] - 3, 0)
  maxDate = new Date(+today[0] + 1, 1)
  current.value = today.slice(0, 2)
  selected.value = today.slice(0, 2)
})

onMounted(() => init())

/**
 * 初始化
 */
const init = () => {
  if (current.value[0] === today[0] && current.value[1] === today[1]) {
    list.value = generateCalendarDates(today)
  } else {
    list.value = generateCalendarDates([...current.value, '01'])
  }
  for (let i = 0, item: DateItem; (item = list.value[i++]); ) {
    props.data && Object.assign(item, props.data[item.value])
  }
}

/**
 * 显示
 */
const handleShow = () => (show.value = true)

/**
 * 确认
 */
const handleConfirm = () => {
  ~(show.value = false) && ~(current.value = [...selected.value])
  emit('confirm', current.value)
}

/**
 * 选择
 */
const handleSelect = (value: DateItem) => {
  if (value.disabled) return
  for (let i = 0, item: DateItem; (item = list.value[i++]); ) {
    item.selected = value.value === item.value ? true : false
  }
  // TODO: 优化，返回更多有效数据
  emit('select', {
    value: value.value,
    status: value.status as number,
    before: value.before,
  })
}

/**
 * 生成日历
 */
const generateCalendarDates = (current: string[]) => {
  const todayYMD = today.join('-')
  const currentYMD = current.join('-')
  const [year, month, date] = current

  // 日期
  const currentDate = +date // 当前天
  const firstDate = formatDate(new Date(+year, +month - 1, 1), 'dd') // 当月第一天
  const lastDate = formatDate(new Date(+year, +month, 0), 'dd') // 当月最后一天
  const prevLastDate = formatDate(new Date(+year, +month - 1, 0), 'dd') // 上个月最后一天

  // 月份
  const currentMonth = +month
  const prevMonth = +month === 1 ? 12 : +month - 1
  const nextMonth = +month === 12 ? 1 : +month + 1

  // 年份
  const currentYear = +year
  const prevYear = prevMonth > currentMonth ? +year - 1 : +year
  const nextYear = nextMonth < currentMonth ? +year + 1 : +year

  // 星期
  const firstDay = new Date(+year, +month - 1, 1).getDay()
  const lastDay = new Date(+year, +month, 0).getDay()

  // 算法
  let totalDays, currentDays, beforeDays, afterDays
  switch (props.mode) {
    case '06':
      totalDays = 6 * 7
      currentDays = +lastDate - +firstDate + 1
      beforeDays = firstDay
      afterDays = totalDays - currentDays - beforeDays
      break
    case '10':
      totalDays = 6 * 7
      currentDays = +lastDate - +firstDate + 1
      beforeDays = firstDay === 0 ? 7 - 1 : firstDay - 1
      afterDays = totalDays - currentDays - beforeDays
      break
  }

  const currentList = [...new Array(currentDays)].map((_, i) => ({
    value: formatDateToString([currentYear, currentMonth, i + 1]),
    date: i + 1,
    today: formatDateToString([currentYear, currentMonth, i + 1]) === todayYMD,
    before:
      formatDateStringToTimestamp(
        formatDateToString([currentYear, currentMonth, i + 1])
      ) < formatDateStringToTimestamp(todayYMD),
    selected:
      formatDateToString([currentYear, currentMonth, i + 1]) === props.default,
    disabled: false,
  }))
  const beforeList = [...new Array(beforeDays)]
    .map((_, i) => ({
      value: formatDateToString([prevYear, prevMonth, +prevLastDate - i]),
      date: +prevLastDate - i,
      disabled: true,
    }))
    .reverse()
  const afterCountList = [...new Array(afterDays)].map((_, i) => ({
    value: formatDateToString([nextYear, nextMonth, i + 1]),
    date: i + 1,
    disabled: true,
  }))

  return [...beforeList, ...currentList, ...afterCountList]
}

// TODO: 改进，将【生成日历】方法逻辑抽离解耦
// const calculateYearMonthDayValues = () => {}

// const calculateDateRange = () => {}

// const createCurrentMonthList = () => {
//   const currentYMD = ''
//   const currentDays = ''
//   const currentYear: any = ''
//   const currentMonth: any = ''

//   return [...new Array(currentDays)].map((_, i) => ({
//     value: formatDateToString([currentYear, currentMonth, i + 1]),
//     date: i + 1,
//     today:
//       formatDateToString([currentYear, currentMonth, i + 1]) === currentYMD,
//     before:
//       formatDateStringToTimestamp(
//         formatDateToString([currentYear, currentMonth, i + 1])
//       ) < formatDateStringToTimestamp(currentYMD),
//     selected:
//       formatDateToString([currentYear, currentMonth, i + 1]) === props.default,
//     disabled: false,
//   }))
// }

// const createPreviousMonthList = () => {}

// const createNextMonthList = () => {}

// /**
//  * 生成日历日期
//  */
// const generateCalendarDates1 = (currentDateStrings: string[]) => {
//   const dateValues = calculateYearMonthDayValues(currentDateStrings)
//   const { currentYMD, firstDay, totalDays, beforeDays, afterDays } =
//     calculateDateRange(dateValues)

//   const currentList = createCurrentMonthList(dateValues, currentYMD)
//   const beforeList = createPreviousMonthList(dateValues, beforeDays)
//   const afterList = createNextMonthList(dateValues, afterDays)

//   return [...beforeList, ...currentList, ...afterList]
// }

/**
 * 格式化显示
 */
const formatPickerDisplay = (type: string, option: Record<string, any>) => {
  switch (type) {
    case 'year':
      option.text += '年'
      break
    case 'month':
      option.text += '月'
      break
  }
  return option
}

/**
 * 格式化（日期转字符串）
 */
const formatDateToString = (value: number[]) => {
  return value.map((i) => i.toString().padStart(2, '0')).join('-')
}

/**
 * 格式化(日期字符串转时间戳)
 */
const formatDateStringToTimestamp = (value: string) => {
  return parseDate(value).getTime()
}
</script>

<template>
  <div class="calendar">
    <div class="header">
      <div class="header__title">{{ currentText }}</div>

      <img
        class="header__icon--arrow"
        src="@/assets/image/20882@2x.png"
        @click="() => handleShow()"
      />
    </div>

    <div class="content">
      <div class="content__row">
        <div v-if="props.mode === '06'" class="content__col">日</div>
        <div class="content__col">一</div>
        <div class="content__col">二</div>
        <div class="content__col">三</div>
        <div class="content__col">四</div>
        <div class="content__col">五</div>
        <div class="content__col">六</div>
        <div v-if="props.mode === '10'" class="content__col">日</div>
      </div>

      <div class="content__row">
        <div
          class="content__col"
          :class="{
            'content__col--selected': item.selected,
          }"
          v-for="(item, index) in list"
          :key="index"
          @click="() => handleSelect(item)"
        >
          <div
            class="content__date"
            :class="{
              'content__date--disable': item.disabled,
              'content__today--single': item.date < 10 && item.today,
              'content__today--dual': item.date >= 10 && item.today,
            }"
          >
            {{ item.date }}
          </div>

          <div
            class="content__text"
            :class="{
              'content__text--opacity': item.before,
              'content__text--color1': item.status === 1,
              'content__text--color2': item.status === 2,
              'content__text--color3': item.status === 3,
            }"
          >
            {{ item.text }}
          </div>
        </div>
      </div>
    </div>

    <van-popup class="popup" v-model:show="show" round position="bottom">
      <van-date-picker
        v-model="selected"
        confirm-button-text="确定"
        :min-date="minDate"
        :max-date="maxDate"
        :formatter="formatPickerDisplay"
        :columns-type="['year', 'month']"
        @cancel="() => (show = false)"
        @confirm="() => handleConfirm()"
      >
        <template #columns-top>
          <div class="popup__content">{{ seletedText }}</div>
        </template>
      </van-date-picker>
    </van-popup>
  </div>
</template>

<style lang="scss" scoped>
.calendar {
  padding: 16px;
  width: 375px;
  height: 420px;
  background: #ffffff;
  font-size: 14px;
  color: #333333;
}

.header {
  display: flex;
  align-items: center;

  &__title {
    font-size: 16px;
    font-weight: 600;
    color: #333333;
  }

  &__icon {
    &--arrow {
      margin-left: 5px;
      width: 20px;
      height: 20px;
    }
  }
}

.content {
  margin-top: 16px;

  &__row {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
  }

  &__row:nth-of-type(1),
  &__col {
    margin-bottom: 0px;
    padding-top: 0px;
    height: 35px;
  }

  &__col {
    display: flex;
    flex-direction: column;
    align-items: center;
    margin-bottom: 3px;
    padding-top: 4px;
    height: 50px;

    &--selected {
      margin: 0 4px;
      background: #994bff;
      box-shadow: 0px 0px 4px 1px rgba(134, 79, 251, 0.2);
      border-radius: 7px 7px 7px 7px;
      div {
        color: #fff;
        opacity: 1;
      }
    }
  }

  &__date {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 20px;
    width: 21px;

    &--disable {
      color: #cccccc;
    }
  }

  &__today {
    &--single {
      background: #994bff;
      border-radius: 50%;
      color: #fff;
      padding: 4px 5.5px 3px;
      text-align: center;
    }

    &--dual {
      background: #994bff;
      border-radius: 50%;
      color: #fff;
      padding: 4px 2.5px 3px 2px;
    }
  }

  &__text {
    margin-top: 10px;
    font-size: 10px;
    font-weight: 400;

    &--color1 {
      color: #994bff;
    }

    &--color2 {
      color: #475bec;
    }

    &--color3 {
      color: #99989b;
    }

    &--opacity {
      opacity: 0.5;
    }
  }
}

.popup {
  &__content {
    margin: 0 auto;
    margin-bottom: 20px;
    width: 316px;
    height: 30px;
    line-height: 30px;
    background: #f4f4f4;
    border-radius: 15px 15px 15px 15px;
    font-weight: 400;
    color: #333333;
    text-align: center;
  }
}

:deep(.van-picker__toolbar) {
  padding: 0 15px;
}

:deep(.van-picker__confirm) {
  color: #994bff;
}
</style>
